//----------------------------头文件依赖----------------------------//
#include "dev_sfc.h"

/* SFC 最高支持 16M SPI NOR FLASH, 因为 SFC 寻址寄存器只有 24 位, 故而限制了寻址范围
 * SFC - AHB 总线,最高频率为系统时钟 1 分频; 		SPI - APB 总线,最高频率为系统时钟 2 分频
 * 
 * 当 SFC Write 时, 会占用 AHB 总线较大的带宽, 
 * 此时若 LCDC / SDRAM 等 AHB 总线外设也在被使用, 会使得 LCDC 无法及时响应/发出信号, 导致驱屏异常闪烁/晃动.
 * 为避免上述情况, 有如下做法供参考 : 
 * 1、尽量通过软件设计/调整预期需求, 避免 LCD 显示 与 SFC Write 同时进行(即 SFC 写入时, 屏幕不进行显示<例:关背光>).
 * 2、转用 IO 模拟 SPI 来 Write SPI_Flash(注意: SFC 与 IO_SPI 之间多次反复的端口初始化切换);
 * 3、减小 SFC Write 单次写入数据量, 增加 SFC Write 总写入次数(即多次调用, 反复短时占用->释放总线, 缓解 LCD 显示需求).
 */

/**
  \brief   	配置 SFC 模块以建立与 SPI-Flash 的通讯
  \param 	[in]   width : 几线通讯(1/2/4)
  \return   /
 */
void sfc_config(uint8_t width)
{
    SFC_InitStructure SFC_initStruct;

    //SFC使用专用的FSPI(Flash SPI)接口连接SPI FLASH
    PORT_Init(PORTD, PIN5, PORTD_PIN5_FSPI_SCLK, 0);
    PORT_Init(PORTD, PIN6, PORTD_PIN6_FSPI_SSEL, 0);
    PORT_Init(PORTD, PIN8, PORTD_PIN8_FSPI_MOSI, 1);
    PORT_Init(PORTD, PIN7, PORTD_PIN7_FSPI_MISO, 1);
    PORT_Init(PORTD, PIN3, PORTD_PIN3_FSPI_DATA2, 1);
    PORT_Init(PORTD, PIN4, PORTD_PIN4_FSPI_DATA3, 1);

    switch (width)
    {
	default: 
		//break;//continues
    case 1:
        SFC_initStruct.ClkDiv = SFC_CLKDIV_2;
        SFC_initStruct.Cmd_Read = 0x03;
        SFC_initStruct.Width_Read = SFC_RDWIDTH_1;
        SFC_initStruct.Cmd_PageProgram = 0x02;
        SFC_initStruct.Width_PageProgram = SFC_PPWIDTH_1;
        break;

    case 2:
        SFC_initStruct.ClkDiv = SFC_CLKDIV_2;
        SFC_initStruct.Cmd_Read = 0xBB;
        SFC_initStruct.Width_Read = SFC_RDWIDTH_2;
        SFC_initStruct.Cmd_PageProgram = 0x02;
        SFC_initStruct.Width_PageProgram = SFC_PPWIDTH_1;
        break;

    case 4:
        SFC_initStruct.ClkDiv = SFC_CLKDIV_2;
        SFC_initStruct.Cmd_Read = 0xEB;
        SFC_initStruct.Width_Read = SFC_RDWIDTH_4;
        SFC_initStruct.Cmd_PageProgram = 0x32;
        SFC_initStruct.Width_PageProgram = SFC_PPWIDTH_4;
        break;
    }
    SFC_Init(&SFC_initStruct);
	
	if (width > 1 && 0 == SFC_QuadState()) 
		SFC_QuadSwitch(1);// 开启 SPI-Flash 的 Quad/QPI Mode(须查看对应型号 spi-flash 是否支持 Quad/QPI 操作)
}

/**
 * @brief	利用芯片的 SFC 将 外挂的SPI-Flash 映射至 MCU 总线地址上
 * @param	/
 * @retval	/
 * @note	配置为内存映射模式
 */
void sfc_init(void)
{
	sfc_config(4);
}

/**
  \brief   	通过 SFC 读取 SPI-Flash ID
  \param 	/
  \return   ID
  \note		须先完成 SFC 的配置, 且 SFC 最大只能读取 uint32_t 范围内的 id 
 */
uint32_t sfc_read_id(void)
{
	uint32_t result = SFC_ReadJEDEC();
	//对 id 做移位处理便于阅读比较
    return ( ( ( result & 0xFF ) << 16 ) | ( ( result & 0xFF00 ) >> 0 ) | ( ( result & 0xFF0000 ) >> 16 ) );
}
